package com.loong.config;


import com.loong.service.impl.MyUserDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableMethodSecurity // 开启权限注解
public class SecurityConfig {
     // 229971210 蒋银辉
    // 将密码加密器添加到容器中，默认密码为123456
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return new MyUserDetailsService();
    }
    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(userDetailsService());
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        return daoAuthenticationProvider;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity security) throws Exception{
        //配置登录认证
        security.formLogin(configurer ->{
                    configurer
                            ////配置登录页面地址
                            .loginPage("/user/login")
                            //配置登录请求处理地址（Post）
                            // 用户在登录页面填写用户名和密码后，表单数据会以 POST 请求的方式发送到 /user/login。
                            // Spring Security 会拦截这个请求，从请求中提取用户名和密码信息。
                            // 注意:这里配置的地址要和页面一致，否则会导致登录失败。
                            .loginProcessingUrl("/user/login")
                            //配置登录请求的用户名和密码参数 这里要和登录界面name属性值保持一致
                            .usernameParameter("username")
                            .passwordParameter("password")
                            //配置登录成功后默认跳转的页面
                            .defaultSuccessUrl("/user/main")
                            //配置登录失败后默认重定向的页面
                            .failureForwardUrl("/user/loginFail");
                }

        );
        // 权限配置
        //配置用户权限
        security.authorizeHttpRequests(registry ->{
            registry
                    //配置不需要认证的路径 +permitall()
                    .requestMatchers("img/**", "/css/**","/user/login", "/user/loginFail", "/user/register", "/register").permitAll()
                    .requestMatchers("/goods").permitAll() // 查看商品列表页面，无需认证
                    .requestMatchers("/purchaser/buy").authenticated()  // 购买商品页面，需要认证登录
                    .requestMatchers("/admin/goods/check", "/admin/shop/check").hasRole("manager") // 审核页面，需要认证管理员角色
                    .requestMatchers("/shopkeeper/publish").hasAnyRole("vip", "manager") // 发布商品页面，需要认证vip和manager角色
                    //除了上面的路径，其他请求都需要认证登录
                    .anyRequest().authenticated();
        });
        //配置CSRF防御
        security.csrf(configurer -> {
            configurer.disable();
        });
        //配置无权限跳转
        security.exceptionHandling(configurer -> {
            //配置无权限跳转页面
            configurer.accessDeniedPage("/user/noauth");
        });
        //配置退出登录
        security.logout(configurer -> {
            configurer
                    .logoutUrl("/logout")
                    .logoutSuccessUrl("/user/login")
                    .invalidateHttpSession(true)
                    .deleteCookies("JSESSIONID");
        });
        //记住我功能配置
        security.rememberMe(configurer -> {
            configurer
                    .userDetailsService(userDetailsService())
                    //修改复选框name值 要和页面name值保持一致
                    .rememberMeParameter("rememberMe")
                    //设置cookie的有效时间
                    .tokenValiditySeconds(3600);
        });
        return security.build();
    }

}